#include "ogl/font/FontLib.h"

Ogl::Font::FontLib::FontLib(const std::string &path)
    :m_Path(path)
{

}

bool Ogl::Font::FontLib::Create()
{
    if (FT_Init_FreeType(&m_Lib))
    {
        wtclog::info("cannot init freetype \n");
        return false;
    }
    if (FT_New_Face(m_Lib, m_Path.c_str(), 0, &m_Face))
    {
        wtclog::info("cannot freetype new face \n");
        return false;
    }
    m_Family = m_Face->family_name;
    //std::cout << "famiilty " << family << std::endl;

    InitResources();
    return true;
}
void Ogl::Font::FontLib::DoneFace()
{
    FT_Done_Face(m_Face);
}

void Ogl::Font::FontLib::DoneFreetype()
{
    FT_Done_FreeType(m_Lib);
}

void Ogl::Font::FontLib::Done()
{
    DoneFace();
    DoneFreetype();
}

bool Ogl::Font::FontLib::HasWChar(const wchar_t &c,int fontSize)
{
    auto charMap = m_CharMap[fontSize];
    return charMap.find(c) != charMap.end();
}

bool Ogl::Font::FontLib::InitResources()
{

    fontMesh = std::make_shared<Ogl::Gut::Mesh>();

    std::vector<glm::vec3> vertices =
    {
        glm::vec3(),
        glm::vec3(),
        glm::vec3(),
        glm::vec3()
    };

    std::vector<glm::vec3> normals =
    {
        glm::vec3(0.f, 0.0f, 1.0f),
        glm::vec3(0.f, 0.0f, 1.0f),
        glm::vec3(0.f, 0.0f, 1.0f),
        glm::vec3(0.f, 0.0f, 1.0f),

    };

    std::vector<glm::vec2> texCoords =
    {
        glm::vec2(0.0, 0.0),
        glm::vec2(0.0, 1.0),
        glm::vec2(1.0, 1.0),
        glm::vec2(1.0, 0.0),

    };

    std::vector<unsigned short> indices = {
        0, 1, 2,
        0, 2, 3 };

    fontMesh->Bind();
    fontMesh->PushVertexBuffer<glm::vec3>(vertices, GL_DYNAMIC_DRAW);
    fontMesh->PushVertexBuffer<glm::vec3>(normals, GL_STATIC_DRAW);
    fontMesh->PushVertexBuffer<glm::vec2>(texCoords, GL_STATIC_DRAW);
    fontMesh->RegisterIndexBuffer<unsigned short>(indices);
    fontMesh->UnBind();


    return true;
}

bool Ogl::Font::FontLib::LoadWstring(const std::wstring& str, int fontSize)
{
    for (const wchar_t &ch : str)
    {
        if (!LoadWChar(ch, fontSize))
        {
            // spdlog::warn("cannot load char {}", ch);
            std::cerr << "cannot load " << ch << std::endl;
            return false;
        }
    }
    return true;
}

void Ogl::Font::FontLib::RenderTextMesh(const std::wstring &text, int fontSize,  float x, float y, float scale)
{
    for (const wchar_t &c : text)
    {
        if (!HasWChar(c, fontSize))
        {
            LoadWChar(c, fontSize);
        }

        auto& charMap = m_CharMap[fontSize];

        Character ch = charMap[c];

        GLfloat xpos = x + ch.m_Bearing.x * scale;
        GLfloat ypos = y - (ch.m_Size.y - ch.m_Bearing.y) * scale;

        GLfloat w = ch.m_Size.x * scale;
        GLfloat h = ch.m_Size.y * scale;

        std::vector<glm::vec3> vertices;
        
        vertices.push_back(glm::vec3(xpos, ypos + h, 0.0f));
        vertices.push_back(glm::vec3(xpos, ypos, 0.0f));
        vertices.push_back(glm::vec3(xpos + w, ypos, 0.0f));
        vertices.push_back(glm::vec3(xpos + w, ypos + h, 0.f));

        auto vertexBufferPos = fontMesh->m_VertexBuffers[0];
        
        fontMesh->Bind();
        vertexBufferPos->Bind();
        //vertexBufferPos->BufferData<glm::vec3>(vertices, GL_DYNAMIC_DRAW);
        fontMesh->RegisterVertexBuffer<glm::vec3>(vertices, 0, GL_DYNAMIC_DRAW);

        if (ch.m_Texture.get())
        {
            ch.m_Texture->Active(0);

        }

        //fontMesh->BufferSubData<VertexFont, unsigned int>(vertices, indices);
        fontMesh->Bind();
        fontMesh->DrawElementsAuto();
        x += (ch.m_Advance >> 6) * scale;
    };

    //glBindVertexArray(0);
    //glBindTexture(GL_TEXTURE_2D, 0);
}


bool Ogl::Font::FontLib::LoadWChar(const wchar_t &c, int fontSize)
{
    FT_Set_Pixel_Sizes(m_Face, 0, fontSize);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Load character glyph
    FT_UInt glyph_index = FT_Get_Char_Index(m_Face, c);
    if (FT_Load_Glyph(m_Face, glyph_index, FT_LOAD_RENDER))
    {
        return false;
    }

    std::shared_ptr<Ogl::Gut::Texture2D> tex;
    // Set texture options
    {
        //Ogl::Gut::Image desc;
        std::shared_ptr<Ogl::Gut::Image> image = 
            std::make_shared<Ogl::Gut::Image>(
                m_Face->glyph->bitmap.width, 
                m_Face->glyph->bitmap.rows,
                1, false, false);
        image->m_Data = m_Face->glyph->bitmap.buffer;
        image->m_DataType = GL_UNSIGNED_BYTE;
        image->m_AutoFreeData = false;


        //desc.target = GL_TEXTURE_2D;
        //desc.level = 0;
        //desc.m_InternalFormat = GL_RED;
        //desc.m_Format= GL_RED;
        //desc.m_Width = m_Face->glyph->bitmap.width;
        //desc.m_Height = m_Face->glyph->bitmap.rows;
        //desc.m_DataType= GL_UNSIGNED_BYTE;
        //desc.m_Data= m_Face->glyph->bitmap.buffer;


        Ogl::Gut::SamplerDesc sampDesc;
        sampDesc.wrapS = GL_CLAMP_TO_EDGE;
        sampDesc.wrapT = GL_CLAMP_TO_EDGE;
        sampDesc.minFilter = GL_LINEAR;
        sampDesc.magFilter = GL_LINEAR;

        tex = std::make_shared<Ogl::Gut::Texture2D>(image,sampDesc);

        //image->Free();

    }

    Character character = {
        tex,
        glm::ivec2(m_Face->glyph->bitmap.width, m_Face->glyph->bitmap.rows),
        glm::ivec2(m_Face->glyph->bitmap_left, m_Face->glyph->bitmap_top),
        (unsigned int)m_Face->glyph->advance.x};

    auto& charMap = m_CharMap[fontSize];
    charMap.insert(std::pair<wchar_t, Character>(c, character));

    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    return true;
}

Ogl::Font::FontLib::~FontLib()
{
    Done();
}